#include <crt_util.h>
#include "compat_event_util.h"

#if (TARGET_OS == OS_WIN)
#include "compat_event_iocp.h"
#elif (TARGET_OS == OS_POSIX)
#include "compat_event_epoll.h"
#elif (TARGET_OS == OS_MACH)
#include "compat_event_kqueue.h"
#elif (TARGET_OS == OS_UNIX)
#include "compat_event_kqueue.h"
#endif

int complete_op(int err, crterr code, evoper_t* oper, evoper_t* oper_s, evevent op)
{
    if (op == op_ev_connect) {
        oper->handler(op_ev_success, oper, oper, op_ev_connect);
    } else if (op == op_ev_open) {
        oper->handler(op_ev_success, oper, oper, op_ev_open);
    } else if (op == op_ev_close) {
        oper->handler(op_ev_success, oper, oper, op_ev_close);
    } else if (op == op_ev_accept) {
        oper->handler(op_ev_success, oper, oper_s, op_ev_accept);
    } else if (op == op_ev_timer) {
        oper->handler(op_ev_success, oper, oper, op_ev_timer);
    } else if (op == op_ev_read) {
        oper->handler(op_ev_success, oper, oper, op_ev_read);
    } else if (op == op_ev_write) {
        oper->handler(op_ev_success, oper, oper, op_ev_write);
    }

    return S_SUCCESS;
}

static void* evmem_heap_malloc(evloop_t* loop, size_t count, size_t size)
{
    return heap_calloc(count, size);
}
static int evmem_heap_free(evloop_t* loop, void* ptr)
{
    heap_free(ptr);
    return S_SUCCESS;
}
static int evloop_addtime(evloop_nodes_t* nodes, evoper_t* oper)
{
    return evloop_nodes_addtime(nodes, oper);
}
static int evloop_deltime(evloop_nodes_t* nodes, evoper_t* oper)
{
    return evloop_nodes_deltime(nodes, oper);
}
static int evloop_gettime(evloop_nodes_t* nodes, evoper_t** oper)
{
    return evloop_nodes_gettime(nodes, oper);
}
static int evsysop_init(evsysop_t* op, int flag)
{
    int rc = S_ERROR;
#if (TARGET_OS == OS_WIN)
    rc = iocp_evop_t(op);
#elif (TARGET_OS == OS_POSIX)
    rc = epoll_evop_t(op);
#elif (TARGET_OS == OS_MACH)
    rc = kqueue_evop_t(op);
#elif (TARGET_OS == OS_UNIX)
    rc = kqueue_evop_t(op);
#endif
    return rc;
}
static int evsysop_bind_handler(evloop_t* loop, evsysop_t* op)
{
    return op->op_handler(loop, complete_op);
}
buf_len evoper_rbyte(evoper_t* oper)
{
    return oper->rbuf.byte;
}
buf_len evoper_wbyte(evoper_t* oper)
{
    return oper->wbuf.byte;
}
int evoper_read_buf(evoper_t* oper, buf_ptr buf, buf_len len)
{
    rc_error(oper != NULL, S_ERROR);
    return evbuf_bind(&oper->rbuf, buf, len, 0);
}
int evoper_write_buf(evoper_t* oper, buf_ptr buf, buf_len len)
{
    rc_error(oper != NULL, S_ERROR);
    return evbuf_bind(&oper->wbuf, buf, len, 0);
}
int evoper_set_msec(evoper_t* oper, crt_msec msec)
{
    rc_error(oper != NULL, S_ERROR);
    oper->msec = msec;
    return S_SUCCESS;
}
int evoper_get_msec(evoper_t* oper, crt_msec* msec)
{
    rc_error(oper != NULL, S_ERROR);
    *msec = oper->msec;
    return S_SUCCESS;
}
int evoper_bind_ctx(evoper_t* oper, void* ctx)
{
    rc_error(oper != NULL, S_ERROR);
    oper->ctx = ctx;
    return S_SUCCESS;
}
int evoper_get_ctx(evoper_t* oper, void** ctx)
{
    rc_error(oper != NULL, S_ERROR);
    *ctx = oper->ctx;
    return S_SUCCESS;
}
int evoper_bind_sock(evoper_t* oper, _sock_t sock)
{
    rc_error(oper != NULL, S_ERROR);
    oper->sock = sock;
    return S_SUCCESS;
}
int evoper_get_sock(evoper_t* oper, _sock_t* sock)
{
    rc_error(oper != NULL, S_ERROR);
    *sock = oper->sock;
    return S_SUCCESS;
}
int evoper_bind_fd(evoper_t* oper, _fd_t fd)
{
    rc_error(oper != NULL, S_ERROR);
    oper->fd = fd;
    return S_SUCCESS;
}
int evoper_get_fd(evoper_t* oper, _fd_t* fd)
{
    rc_error(oper != NULL, S_ERROR);
    *fd = oper->fd;
    return S_SUCCESS;
}
int evoper_bind_type(evoper_t* oper, evtype type)
{
    rc_error(oper != NULL, S_ERROR);
    oper->t = type;
    return S_SUCCESS;
}
int evoper_get_type(evoper_t* oper, evtype* type)
{
    rc_error(oper != NULL, S_ERROR);
    *type = oper->t;
    return S_SUCCESS;
}
int evoper_bind_evloop(evoper_t* oper, evloop_t* loop)
{
    rc_error(loop != NULL, S_ERROR);
    rc_error(oper != NULL, S_ERROR);
    oper->loop = loop;
    return S_SUCCESS;
}
int evoper_get_evloop(evoper_t* oper, evloop_t** evloop)
{
    rc_error(oper != NULL, S_ERROR);
    *evloop = oper->loop;
    return S_SUCCESS;
}
int evoper_sock(evoper_t* oper)
{
    _sock_t sock = INVALID_SOCKET;
    sock = _createsock(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    rc_error(sock != INVALID_SOCKET, S_ERROR);
    return evoper_assign_sock(oper, sock);
}
int evoper_pollsock(evoper_t* oper, const evaddr_t* addr, int listen)
{
    int rc = S_ERROR;
    rc_error(addr != NULL, S_ERROR);
    rc_error(addr->ptr != NULL, S_ERROR);
    rc_error(addr->len != 0, S_ERROR);

    _sock_t sock;

    sock = INVALID_SOCKET;
    sock = _createsock(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    rc_error(sock != INVALID_SOCKET, S_ERROR);

    rc = set_nosigpipe(sock, 1);
    rc_error(rc == S_SUCCESS, S_ERROR);

    rc = _bind_stcpsockv4(sock, addr->ptr, addr->len, listen, addr->port & UINT_MAX);
    rc_error(rc == S_SUCCESS, S_ERROR);

    rc = set_blocking(sock, 1);
    rc_error(rc == S_SUCCESS, S_ERROR);

    evoper_bind_sock(oper, sock);
    rc_error(rc == S_SUCCESS, S_ERROR);

    rc = evoper_bind_type(oper, ev_sockpoll);
    rc_error(rc == S_SUCCESS, S_ERROR);

    return S_SUCCESS;
}
int evoper_bind_handler(evoper_t* oper, ev_handler handler)
{
    rc_error(oper != NULL, S_ERROR);
    oper->handler = handler;
    return S_SUCCESS;
}
int evloop_set_status(evloop_t* loop, evloop_status st)
{
    rc_error(loop != NULL, S_ERROR);
    loop->status = st;
    return S_SUCCESS;
}
evloop_status evloop_get_status(evloop_t* loop)
{
    rc_error(loop != NULL, evloop_null);
    return loop->status;
}
int evloop_update_time(evloop_t* loop)
{
    rc_error(loop != NULL, S_ERROR);
    return evloop_clock_update(loop->clock);
}
int evloop_set_clock(evloop_t* loop, crt_msec msec)
{
    rc_error(loop != NULL, S_ERROR);
    evloop_clock_setmsec(loop->clock, msec);
    return S_SUCCESS;
}
int evloop_get_clock(evloop_t* loop, crt_msec* msec)
{
    rc_error(loop != NULL, 0);
    rc_error(loop->clock != NULL, 0);
    return evloop_clock_getmsec(loop->clock, msec);
}
int evloop_bind_ctx(evloop_t* loop, void* ctx)
{
    rc_error(loop != NULL, S_ERROR);
    loop->ctx = ctx;
    return S_SUCCESS;
}
int evloop_get_ctx(evloop_t* loop, void** ctx)
{
    rc_error(loop != NULL, S_ERROR);
    *ctx = loop->ctx;
    return S_SUCCESS;
}
int evloop_bind_mmctx(evloop_t* loop, void* mmctx)
{
    rc_error(loop != NULL, S_ERROR);
    loop->mmctx = mmctx;
    return S_SUCCESS;
}
int evloop_get_mmctx(evloop_t* loop, void** mmctx)
{
    rc_error(loop != NULL, S_ERROR);
    *mmctx = loop->mmctx;
    return S_SUCCESS;
}
int evloop_add_timer(evloop_t* loop, evoper_t* oper, ev_time_t tv)
{
    rc_error(oper != NULL, S_ERROR);
    rc_error(loop != NULL, S_ERROR);
    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);

    evoper_bind_type(oper, ev_time);
    evoper_bind_evloop(oper, loop);
    evloop_update_time(loop);
    crt_msec msec;
    evloop_clock_getmsec(loop->clock, &msec);
    oper->msec = tv_to_msec(tv) + msec;
    evloop_addtime(loop->nodes, oper);
    return S_SUCCESS;
}
int evloop_enable_read(evloop_t* loop, evoper_t* oper, short event, socklen_t size, ev_time_t tv)
{
    rc_error(oper != NULL, S_MEMERROR);
    rc_error(evoper_connected(oper) == S_SUCCESS, S_CONNERROR);
    rs_error(evoper_erring(oper) == S_SUCCESS, S_ERROR);
    rs_error(evoper_closeing(oper) == S_SUCCESS, S_CLOSE);
    rc_error(loop != NULL, S_MEMERROR);
    rc_error(evoper_reading(oper) != S_SUCCESS, S_PENDING);
    evoper_read_op(oper, op_ev_reading);

    rc_error(loop != NULL, S_MEMERROR);

    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    rc_error(evsel->op_enable_read != NULL, S_ERROR);

    set_recvlowat(oper->sock, event == op_ev_level ? size : 1);
  
    return evsel->op_enable_read(loop, oper, event, tv);
}
int evloop_enable_write(evloop_t* loop, evoper_t* oper, short event, socklen_t size, ev_time_t tv)
{
    rc_error(oper != NULL, S_MEMERROR);
    rc_error(evoper_connected(oper) == S_SUCCESS, S_CONNERROR);
    rs_error(evoper_erring(oper) == S_SUCCESS, S_ERROR);
    rs_error(evoper_closeing(oper) == S_SUCCESS, S_CLOSE);
    rc_error(loop != NULL, S_MEMERROR);
    rc_error(evoper_writeing(oper) != S_SUCCESS, S_PENDING);
    evoper_write_op(oper, op_ev_writeing);

    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    rc_error(evsel->op_enable_write != NULL, S_ERROR);

    return evsel->op_enable_write(loop, oper, event, tv);
}
int evloop_disable_read(evloop_t* loop, evoper_t* oper)
{
    rc_error(oper != NULL, S_MEMERROR);
    rc_error(evoper_connected(oper) == S_SUCCESS, S_CONNERROR);
    rs_error(evoper_erring(oper) == S_SUCCESS, S_ERROR);
    rs_error(evoper_closeing(oper) == S_SUCCESS, S_CLOSE);
    rc_error(loop != NULL, S_MEMERROR);
    evoper_read_op(oper, op_ev_read);

    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    rc_error(evsel->op_disable_read != NULL, S_ERROR);
    return evsel->op_disable_read(loop, oper, op_ev_read);
}

int evloop_disable_write(evloop_t* loop, evoper_t* oper)
{
    rc_error(oper != NULL, S_MEMERROR);
    rc_error(evoper_connected(oper) == S_SUCCESS, S_CONNERROR);
    rs_error(evoper_erring(oper) == S_SUCCESS, S_ERROR);
    rs_error(evoper_closeing(oper) == S_SUCCESS, S_CLOSE);
    rc_error(loop != NULL, S_MEMERROR);
    evoper_write_op(oper, op_ev_write);

    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    rc_error(evsel->op_disable_write != NULL, S_ERROR);

    return evsel->op_disable_write(loop, oper, op_ev_write);
}
int evloop_canncel(evloop_t* loop, evoper_t* oper)
{
    rc_error(oper != NULL, S_MEMERROR);
    rc_error(evoper_connected(oper) == S_SUCCESS, S_CONNERROR);
    rs_error(evoper_erring(oper) == S_SUCCESS, S_ERROR);
    rs_error(evoper_closeing(oper) == S_SUCCESS, S_CLOSE);
    rc_error(loop != NULL, S_MEMERROR);

    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    rc_error(evsel->op_cancel != NULL, S_ERROR);

    return evsel->op_cancel(loop, oper);
}
int evloop_open(evoper_t* oper, const evaddr_t* addr, ev_time_t tv)
{
    rc_error(oper != NULL, S_ERROR);
    evloop_t* loop = oper->loop;
    rc_error(loop != NULL, S_ERROR);

    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    rc_error(evsel->op_open != NULL, S_ERROR);

    return evsel->op_open(loop, oper, addr);
}
int evloop_poll(evoper_t* oper, unsigned int ls)
{
    rc_error(oper != NULL, S_ERROR);
    evloop_t* loop = oper->loop;
    rc_error(loop != NULL, S_ERROR);
    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    rc_error(evsel->op_poll != NULL, S_ERROR);

    rs_error(oper->accept == op_ev_accepting, S_COMPLETED) rs_error(oper->accept == op_ev_accepted, S_CLOSE) evoper_listen_size(oper, ls);
    evoper_accept_op(oper, op_ev_accepting);

    return evsel->op_poll(loop, oper);
}
int evloop_add_connect(evoper_t* oper, const evaddr_t* addr, ev_time_t tv)
{
    int rc = S_ERROR;
    rc_error(oper != NULL, S_ERROR);
    evloop_t* loop = oper->loop;
    rc_error(loop != NULL, S_ERROR);

    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    rc_error(evsel->op_add_connect != NULL, S_ERROR);

    rs_error(oper->conn == op_ev_connected, S_COMPLETED) evoper_connect_op(oper, op_ev_connecting);

    rc = evsel->op_add_connect(loop, oper, addr, tv);
    rs_error(rc == S_PENDING, S_SUCCESS);

    if (rc == S_COMPLETED) {
        evoper_connect_op(oper, op_ev_connected);
        complete_op(S_SUCCESS, 0, oper, oper, op_ev_connect);
    }

    return rc;
}
int evloop_del_connect(evoper_t* oper)
{
    rc_error(oper != NULL, S_ERROR);
    evloop_t* loop = oper->loop;
    rc_error(loop != NULL, S_ERROR);

    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    rc_error(evsel->op_del_connect != NULL, S_ERROR);

    return evsel->op_del_connect(loop, oper);
}
int evloop_alloc_evoper(evloop_t* loop, evoper_t* oper)
{
    rc_error(oper != NULL, S_ERROR);
    rc_error(loop != NULL, S_ERROR);
    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    rc_error(evsel->op_alloc_evoper != NULL, S_ERROR);

    evoper_bind_evloop(oper, loop);

    evbuf_init(&oper->rbuf);
    evbuf_init(&oper->wbuf);

    return evsel->op_alloc_evoper(loop, oper);
}
int evloop_dealloc_evoper(evloop_t* loop, evoper_t* oper)
{
    rc_error(oper != NULL, S_ERROR);
    rc_error(loop != NULL, S_ERROR);
    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    rc_error(evsel->op_dealloc_evoper != NULL, S_ERROR);

    return evsel->op_dealloc_evoper(loop, oper);
}
int evloop_init(evloop_t* loop, int size, int flag)
{
    int rc = S_ERROR;
    rc_error(loop != NULL, S_ERROR);

    loop->status = evloop_null;
    loop->sysop = NULL;
    loop->evbase = NULL;
    loop->nodes = NULL;
    loop->clock = NULL;
    loop->ctx = NULL;
    loop->mmctx = NULL;
    loop->mm_malloc = NULL;
    loop->mm_free = NULL;
    loop->handler = NULL;

    loop->mm_malloc = evmem_heap_malloc;
    loop->mm_free = evmem_heap_free;

    loop->sysop = loop->mm_malloc(loop, 1, sizeof(evsysop_t));
    rc_error(loop->sysop != NULL, S_MEMERROR);

    evsysop_init(loop->sysop, flag);
    rc = loop->sysop->op_alloc(loop, size);
    rc_error(rc != S_ERROR, S_SUPORTERROR);
    rc_error(loop->evbase != NULL, S_MEMERROR);
    evsysop_bind_handler(loop, loop->sysop);

    loop->nodes = loop->mm_malloc(loop, 1, sizeof(evloop_nodes_t));
    rc_error(loop->nodes != NULL, S_MEMERROR);
    evloop_nodes_init(loop->nodes);

    loop->queue = loop->mm_malloc(loop, 1, sizeof(evloop_queue_t));
    rc_error(loop->queue != NULL, S_MEMERROR);
    evloop_queue_init(loop->queue);

    loop->clock = loop->mm_malloc(loop, 1, sizeof(evloop_clock_t));
    rc_error(loop->clock != NULL, S_MEMERROR);
    evloop_clock_init(loop->clock);

    evloop_update_time(loop);

    return S_SUCCESS;
}
int evloop_uninit(evloop_t* loop)
{
    rc_error(loop != NULL, S_ERROR);
    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    evsel->op_dealloc(loop);
    heap_free(loop->sysop);

    evloop_clock_uninit(loop->clock);
    heap_free(loop->clock);

    evloop_nodes_uninit(loop->nodes);
    heap_free(loop->nodes);

    loop->status = evloop_null;
    loop->sysop = NULL;
    loop->evbase = NULL;
    loop->nodes = NULL;
    loop->clock = NULL;
    loop->ctx = NULL;
    loop->mmctx = NULL;
    loop->mm_malloc = NULL;
    loop->mm_free = NULL;

    return S_SUCCESS;
}

static int cmd_poll_time(evloop_t* loop, evoper_t* oper)
{
    int rc = S_SUCCESS;
    rc_error(loop != NULL, S_ERROR);
    const evsysop_t* op = evloop_sysop(loop);
    rc_error(op != NULL, S_ERROR);

    crt_msec ms = 0;
    crt_msec msec = 0;
    crt_msec oper_msec = 0;
    uint64_t node_count = 0;

    rc = evloop_nodes_count(loop->nodes, &node_count);
    rc_error(rc != S_ERROR, S_ERROR) rc_error(node_count != 0, S_ERROR);

    rc = evloop_deltime(loop->nodes, oper);
    rc_error(rc != S_ERROR, S_ERROR) rc_error(oper != NULL, S_ERROR);

    rc = evoper_get_msec(oper, &oper_msec);
    rc_error(rc != S_ERROR, S_ERROR) rc_error(oper_msec != 0, S_ERROR);

    rc = evloop_clock_getmsec(loop->clock, &msec);
    rc_error(rc != S_ERROR, S_ERROR) rc_error(msec != 0, S_ERROR);

    ms = oper_msec - msec;

    if (ms > 0)
        op->op_dispatch(loop, op_cmd_poll, ms);

    complete_op(ms > 0 ? S_SUCCESS : S_ERROR, 0, oper, oper, op_ev_timer);

    return S_SUCCESS;
}
static int cmd_poll(evloop_t* loop, crt_msec msec)
{
    rc_error(loop != NULL, S_ERROR);
    rc_error(loop != NULL, S_ERROR);
    const evsysop_t* op = evloop_sysop(loop);
    rc_error(op != NULL, S_ERROR);
    rc_error(op->op_dispatch != NULL, S_ERROR);
    op->op_dispatch(loop, op_cmd_poll, msec);
    return S_SUCCESS;
}
int evloop_run(evloop_t* loop, crt_msec msec)
{
    rc_error(loop != NULL, S_ERROR);
    const evsysop_t* op = evloop_sysop(loop);
    rc_error(op != NULL, S_ERROR);
    int rc = S_ERROR;
    loop->status = evloop_running;
    while (loop->status != evloop_close) {
        evoper_t* oper = NULL;
        int ret = S_NOFOUND;
        ret = evloop_gettime(loop->nodes, &oper);
        evloop_update_time(loop);
        rc = (ret == S_FOUND ? cmd_poll_time(loop, oper) : cmd_poll(loop, msec));
        evloop_update_time(loop);
    }
    return S_SUCCESS;
}

int evloop_stop(evloop_t* loop)
{
    rc_error(loop != NULL, S_ERROR);
    const evsysop_t* evsel = evloop_sysop(loop);
    rc_error(evsel != NULL, S_ERROR);
    return evsel->op_dispatch(loop, op_cmd_exit, 0);
}
int evoper_alloc(evoper_t** oper, void* ctx, void* (*alloc)(void* ctx, size_t size))
{
    *oper = (alloc == NULL ? heap_malloc(sizeof(evoper_t)) : alloc(ctx, sizeof(evoper_t)));
    return S_SUCCESS;
}
int evoper_free(evoper_t* oper, void* ctx, int (*free)(void* ctx, void* ptr))
{
    free == NULL ? heap_free(oper) : free(ctx, oper);
    return S_SUCCESS;
}
int evloop_alloc(evloop_t** loop, void* ctx, void* (*alloc)(void* ctx, size_t size))
{
    *loop = (alloc == NULL ? heap_malloc(sizeof(evloop_t)) : alloc(ctx, sizeof(evloop_t)));
    return S_SUCCESS;
}
int evloop_free(evloop_t* loop, void* ctx, int (*free)(void* ctx, void* ptr))
{
    free == NULL ? heap_free(loop) : free(ctx, loop);
    return S_SUCCESS;
}
int evloop_bind_memory(evloop_t* loop, evmem_malloc malloc, evmem_free free, void* mmctx)
{
    rc_error(loop != NULL, S_ERROR);
    loop->mm_malloc = malloc;
    loop->mm_free = free;
    loop->mmctx = mmctx;
    return S_SUCCESS;
}
int evloop_bind_event(evloop_t* loop, evloop_handler handler)
{
    rc_error(loop != NULL, S_ERROR);
    loop->handler = handler;
    return S_SUCCESS;
}
